﻿using System;
using System.Drawing;
using System.Text;
using Microsoft.Office.Interop.PowerPoint;

namespace Microsoft.Office.PowerPoint.STAMP.Core
{
    /// <summary>
    /// Performs layout tasks on slides.
    /// </summary>
    public class LayoutManager
    {
        private readonly StampSlide _slide;
        private readonly StampMediaShape _mediaShape;
        private readonly CaptionStyle _captionStyle;

        /// <summary>
        /// Initializes a new instance of the <see cref="LayoutManager"/> class.
        /// </summary>
        /// <param name="slide">The slide.</param>
        /// <param name="mediaShape">The media shape.</param>
        /// <param name="captionStyle">The caption style.</param>
        public LayoutManager(StampSlide slide, StampMediaShape mediaShape, CaptionStyle captionStyle)
        {
            _slide = slide;
            _captionStyle = captionStyle;
            _mediaShape = mediaShape;
        }

        /// <summary>
        /// Determines the caption text box position.
        /// </summary>
        /// <param name="captionText">The caption text.</param>
        /// <param name="fontSize"></param>
        /// <returns></returns>
        public RectangleF DetermineCaptionTextBoxPosition(string captionText, float fontSize)
        {
            Slide slide = _slide.Slide;
            Shape mediaShape = _mediaShape.Shape;
            RectangleF calculatedRectangle = new RectangleF();

            RectangleF mediaRectangle = new RectangleF(mediaShape.Left, mediaShape.Top, mediaShape.Width,
                                                       mediaShape.Height);
            RectangleF slideRectangle = new RectangleF(0, 0, slide.Application.ActivePresentation.PageSetup.SlideWidth,
                                                       slide.Application.ActivePresentation.PageSetup.SlideHeight);
            string maxLineText = GenerateMaxLineText();
            SizeF maximumShapeSize = DetermineShapeSize(slideRectangle, mediaRectangle, _captionStyle, maxLineText, slide, fontSize);

            SizeF textShapeSize = DetermineShapeSize(slideRectangle, mediaRectangle, _captionStyle, captionText, slide, fontSize);

            calculatedRectangle.Size = new SizeF(textShapeSize.Width, Math.Min(textShapeSize.Height, maximumShapeSize.Height));

            calculatedRectangle.Location = DetermineShapeLocation(slideRectangle, mediaRectangle, _captionStyle.CaptionLocation,
                                                                  calculatedRectangle.Size);

            return calculatedRectangle;
        }

        /// <summary>
        /// Determines the size of the item
        /// </summary>
        private static SizeF DetermineShapeSize(RectangleF slideRectangle, RectangleF mediaRectangle,
                                               CaptionStyle captionStyle, string text, Slide slide, float fontSize)
        {
            SizeF shapeSize = new SizeF
            {
                Width = DetermineShapeWidth(captionStyle.CaptionLocation, slideRectangle.Width,
                                            mediaRectangle.Width)
            };

            Shape tempShape = Utils.ObtainTempShape(slide);
            tempShape.Width = shapeSize.Width;
            StampCaptionShape tempCaptionShape = new StampCaptionShape(tempShape) { Text = text };
            tempCaptionShape.ApplyStyle(captionStyle, fontSize, false);
            tempCaptionShape.Visible = false;
            tempShape.TextFrame.AutoSize = PpAutoSize.ppAutoSizeShapeToFitText;

            SizeF sizeF = new SizeF(tempShape.TextFrame.TextRange.BoundWidth, tempShape.TextFrame.TextRange.BoundHeight);
            SizeF textSize = sizeF;
            shapeSize.Height = textSize.Height;

            return shapeSize;
        }

        /// <summary>
        /// Checks which width to use based on the caption location element
        /// </summary>
        /// <param name="captionLocation"></param>
        /// <param name="slideWidth"></param>
        /// <param name="mediaWidth"></param>
        /// <returns></returns>
        private static float DetermineShapeWidth(CaptionLocation captionLocation, float slideWidth, float mediaWidth)
        {
            float width = captionLocation == CaptionLocation.VideoBottom
                              ? mediaWidth
                              : slideWidth;
            return width;
        }

        /// <summary>
        /// Gets the caption location.
        /// </summary>
        /// <param name="mediaShape">The media shape.</param>
        /// <returns></returns>
        private static CaptionLocation GetCaptionLocation(StampMediaShape mediaShape)
        {
            var location = CaptionLocation.SlideBottom;
            if (mediaShape.IsMovie)
            {
                location = CaptionLocation.VideoBottom;

                if (mediaShape.Width < new Inch(4).ToPixels())
                    location = CaptionLocation.SlideBottom;
            }
            return location;
        }

        private static string GenerateMaxLineText()
        {
            StringBuilder builder = new StringBuilder();
            for (int i = 1; i < Properties.Settings.Default.MaximumNumberOfLines; i++)
            {
                builder.AppendLine("A");
            }
            builder.Append("A");
            return builder.ToString();
        }

        /// <summary>
        /// Determines the shape location based on the CaptionLocation property
        /// </summary>
        private static PointF DetermineShapeLocation(RectangleF slideRectangle, RectangleF mediaRectangle,
                                                     CaptionLocation captionLocation, SizeF shapeSize)
        {
            float yCoordinate = DetermineShapeYPositionByLocation(captionLocation, slideRectangle,
                                                                                        mediaRectangle,
                                                                                        shapeSize.Height);
            float xCoordinate = captionLocation == CaptionLocation.VideoBottom
                          ? mediaRectangle.X
                          : slideRectangle.X;
            return new PointF { Y = yCoordinate, X = xCoordinate };
        }

        /// <summary>
        /// Determines the Y / Top position of where to place the item by the CaptionLocation
        /// </summary>
        /// <param name="captionLocation"></param>
        /// <param name="slideRectangle"></param>
        /// <param name="mediaRectangle"></param>
        /// <param name="shapeHeight"></param>
        /// <returns></returns>
        private static float DetermineShapeYPositionByLocation(CaptionLocation captionLocation,
                                                               RectangleF slideRectangle, RectangleF mediaRectangle,
                                                               float shapeHeight)
        {
            float position = captionLocation == CaptionLocation.VideoBottom
                                 ? DetermineShapeYPosition(mediaRectangle.Height, mediaRectangle.Top, shapeHeight)
                                 : DetermineShapeYPosition(slideRectangle.Height, slideRectangle.Top, shapeHeight);
            return position;
        }

        /// <summary>
        /// Works out the Y / Top position so that the item will fit correctly at the bottom of the container 
        /// </summary>
        /// <param name="containerHeight"></param>
        /// <param name="top"></param>
        /// <param name="shapeHeight"></param>
        /// <returns></returns>
        private static float DetermineShapeYPosition(float containerHeight, float top, float shapeHeight)
        {
            float position = containerHeight + top - shapeHeight;
            return position;
        }

        /// <summary>
        /// Determines the caption location.
        /// </summary>
        /// <returns></returns>
        public CaptionLocation DetermineCaptionLocation()
        {
            return _mediaShape.CaptionAlignment == CaptionAlignment.Auto ? GetCaptionLocation(_mediaShape) : CaptionLocation.SlideBottom;
        }
    }
}